/*
 * This file is part of the Try! Engine project.
 *
 * Copyright 2010-2011 Emanuele Bertoldi. All rights reserved.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * You should have received a copy of the modified BSD License along with this
 * program. If not, see <http://www.opensource.org/licenses/bsd-license.php>
 */

#ifndef TRY_SCENENODE_H
#define TRY_SCENENODE_H

#include "graphics.h"
#include <listenable.h>
#include <map>

namespace Try
{
    class SceneNode : public Transformable, public Listenable<SceneNodeListener>
    {
    public:
        enum SceneNodeFlags
        {
            NoDraw          = 1,
            NoCastShadow    = 2,
            NoRayQuery      = 4,
            Inactive        = 7  // NoDraw | NoCastShadow | NoRayQuery
        };

    protected:
        NodeHandle m_node;
        int m_childrenCount;

    private:
        typedef std::map<NodeHandle, SceneNode*> SceneNodeLookup;
        static SceneNodeLookup s_nodes;
        static SceneNode* s_root;

    public:
        SceneNode(
                const String& name,
                const Transform& t = Transform(),
                SceneNode* parent = SceneNode::rootNode());
        virtual ~SceneNode();

        void setName(const String& name);
        void setParent(SceneNode* parent);

        void resetTransform();
        void setTransform(const Transform& t);
        void setTransform(
            const Vector3& pos,
            const Vector3& rot,
            const Vector3& scale
        );
        void lookAt(
            const Vector3& target,
            const Vector3& up = Vector3::YAxis
        );

        void setFlags(int flags, bool recursive = true);
        void setFlag(SceneNodeFlags flag, bool value, bool recursive = true);

        String name() const;
        Transform transform() const;
        AxisAlignedBoundingBox worldAABB() const;

        SceneNode* parent() const;
        SceneNode* child(int index) const;
        bool hasChild(SceneNode* node) const;
        int childrenCount() const;

        int flags() const;
        bool hasFlags(int flags) const;

        static SceneNode* rootNode();

        friend class GraphicsManager;

    protected:
        SceneNode(const NodeHandle& node);

        void setNodeHandle(const NodeHandle& n);

        NodeHandle nodeHandle() const;

        static NodeHandle rootNodeHandle();
        static bool isNodeHandleRegistered(const NodeHandle& node);
        static SceneNode* nodeFromHandle(const NodeHandle& node);
        static SceneNode* parentFromHandle(const NodeHandle& node);
    };

    typedef std::vector<SceneNode*> SceneNodeList;
}

#endif // TRY_MESH3D_H
